package com.app.zenmewan.commons;

import com.app.zenmewan.entity.*;
import com.app.zenmewan.service.*;
import com.sun.org.apache.bcel.internal.generic.ALOAD;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;

@Component
public class Act {
    @Resource
    private UserStatsticService userStatsticService;
    @Resource
    private UserService userService;
    @Resource
    private ArticleService articleService;
    @Resource
    private ScenCommentService scenCommentService;
    @Resource
    private SpotService spotService;
    private int artilce_len;

    /**
     * 建立user矩阵,并计算相似度和喜好程度,
     * offset，起点
     * limit, 数量
     * 用于分页
     */
    public ArrayList<Article> getList(int user_id, int offset, int limit) {

        List<Article> artlist = this.articleService.queryAll(new Article());//攻略文章列表
        //如果超过限制，则直接结束
        if (offset > artlist.size()) {
            return null;
        }
        List<UserStatstic> l = this.userStatsticService.queryAll(new UserStatstic());
        List<User> ulist = this.userService.queryAll(new User());//用户列表
        List<double[]> m = new ArrayList<>();//用户关于文章的矩阵
        int len_article = artlist.size();//文章数量
        this.artilce_len = len_article;
        //id对应下标
        HashMap<Integer, Integer> artmap = new HashMap<>();
        HashMap<Integer, Integer> reartmap = new HashMap<>();
        HashMap<Integer, Integer> usermap = new HashMap<>();
        //初始化矩阵，每个变量都为0
        for (int i = 0; i < ulist.size(); i++) {
            usermap.put(ulist.get(i).getId(), i);
            m.add(new double[len_article]);
        }
        for (int i = 0; i < artlist.size(); i++) {
            artmap.put(artlist.get(i).getId(), i);
            reartmap.put(i, artlist.get(i).getId());
        }
        //载入用户对于文章的兴趣程度
        //利用关系表载入  l
        for (int i = 0; i < l.size(); i++) {
            UserStatstic us = l.get(i);
            /**
             * 喜好程度：
             * 默认加0.2 因为只有点赞或者收藏必定是看过文章，它对于改文章的喜好可以加0.2
             * 收藏或者点赞都会加0.4
             * 喜好程度最大值为1
             */
            double xh = 0.2 + (us.getShoucang() * 0.4) + (us.getDianzan() * 0.4);
            m.get(usermap.get(us.getUid()))[artmap.get(us.getAid())] = xh;
        }
        double Xu[] = m.get(usermap.get(user_id));

        double Xl2 = L2(Xu);
        HashMap<Integer, Double> ui_a = new HashMap<>(); //user_i , value为相似度
        for (int i = 0; i < m.size(); i++) {
            double xiangsidu = (diancheng(Xu, m.get(i)) / (Xl2 * L2(m.get(i))));
//            System.out.println("Xu and X" + (i + 1) + ":" + xiangsidu); //相似度计算
            ui_a.put(i, xiangsidu);
        }
        //需要排除自己的向量，这里的操作直接自己的向量相似度变为0
        ui_a.put(usermap.get(user_id), 0.0);
        //遍历所有的用户相似度，获取对应的文章喜好程度
        //文章喜好程度相乘用户相似度,进行排序
        int tuijianlist[] = getSortByxiangsidu(m, ui_a); //获取表格
        ArrayList<Article> list = new ArrayList<>();
        for (int i = offset, ci = 0; i < tuijianlist.length && ci < limit; i++, ci++) {
            list.add(this.articleService.queryById(reartmap.get(tuijianlist[i])));
        }
        return list;
    }

    public ArrayList<Spot> getSpots(int sid) {
        //获取所有的点评情况
        List<ScenComment> comments = this.scenCommentService.queryAll(new ScenComment());
        //放置已经点评过的sid
        HashSet<Integer> sids = new HashSet<>();
        //获取用户列表
        List<User> users = this.userService.queryAll(new User());
        //矩阵m X为景点向量，Xi为景点的Useri的好评程度
        List<double[]> m = new ArrayList<>();
        //hashmap
        HashMap<Integer, Integer> spot_index = new HashMap<>();
        HashMap<Integer, Integer> re_spot_index = new HashMap<>();
        //初始化sids
        for (ScenComment comment : comments) {
            sids.add(comment.getSid());
        }
        //初始化矩阵
        int ci = 0;
        for (Integer i_sid : sids) {
            re_spot_index.put(ci, i_sid);
            spot_index.put(i_sid, ci++);
            double dlist[] = new double[users.size()];//满意度向量
            for (int j = 0; j < users.size(); j++) {
                for (ScenComment comment : comments) {
                    double result = 0;
                    if (comment.getSid() == i_sid && comment.getUid() == users.get(j).getId()) {
                        result = comment.getRate();
                        dlist[j] = result / 5;
                        break;
                    }
                    dlist[j] = result;
                }
            }
            m.add(dlist);
        }
        double s_xiangsidu[] = new double[m.size()];
        //获取本sid的向量
        double s2[] = m.get(spot_index.get(sid));
        double sl2 = L2(s2);//范数
        for (int i = 0; i < m.size(); i++) {
            double xiangsidu = (diancheng(s2, m.get(i))) / (sl2 * L2(m.get(i)));
            s_xiangsidu[i] = xiangsidu;
        }
        int l[] = getIndexSortList(s_xiangsidu);//排序好的index
        ArrayList<Spot> list = new ArrayList<>();
        for (int i = 0; i < l.length; i++) {
            if(re_spot_index.get(l[i])==sid){
                continue;
            }
            Spot s = new Spot();
            s.setSid(re_spot_index.get(l[i]));
            List<Spot> fintspots = this.spotService.queryAll(s);
            if (fintspots.size() > 0)
                list.add(fintspots.get(0));
        }
//        System.out.println();
        return list;
    }

    /**
     * 两个向量的点成
     */
    private double diancheng(double[] x, double[] y) {
        double count = 0;
        for (int i = 0; i < x.length; i++) {
            count += x[i] * y[i];
        }
        return count;
    }

    /**
     * 向量的二范数
     */
    private double L2(double[] x) {
        double c = 0;
        for (int i = 0; i < x.length; i++) {
            c += x[i] * x[i];
        }
        return Math.sqrt(c);
    }

    /**
     * 功能: 计算喜好程度
     *
     * @param m   矩阵，X为user的向量,Xi为user的对于文章喜好程度
     * @param map 相似度，key为下标，value为相似度
     * @return 返回下标顺序表
     */
    private int[] getSortByxiangsidu(List<double[]> m, HashMap<Integer, Double> map) {
        //建立userm 对article的喜好向量
        double items[] = new double[this.artilce_len];
        double c = 0;//map的values的总和
        for (Object o : map.values().toArray()) {
            c += (double) o;
        }
        //如果没有相似的程度，则直接结束
        if (0 == c) {
            return null;
        }
//        System.out.println("the sum of the map values : " + c);
        for (int i = 0; i < items.length; i++) {
            for (int i1 = 0; i1 < m.size(); i1++) {
                items[i] += m.get(i1)[i] * map.get(i1);
            }
            items[i] = items[i] / c;
        }
//        System.out.println("the item like list: ");
        for (double item : items) {
//            System.out.print(item + "\n");
        }
        int[] result = getIndexSortList(items);
        return result;
    }

    /**
     * 返回按大小顺序的下标列表
     * 这个获取下标，前提是不能换顺序，只能一个一个的读
     *
     * @param l 有值的数组
     * @return
     */
    private int[] getIndexSortList(double[] l) {
        HashMap<Double, ArrayList<Integer>> map = new HashMap<>();
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i < l.length; i++) {
            if (!map.containsKey(l[i])) {
                map.put(l[i], new ArrayList<>());
            }
            map.get(l[i]).add(i);
        }
        Arrays.sort(l);
        reverse(l);//反转
        //处理index
        for (int i = 0; i < l.length; i++) {
            if (map.containsKey(l[i])) {
                list.addAll(map.get(l[i]));
            }
            map.remove(l[i]);
        }
        int[] result = new int[list.size()];
        for (int i = 0; i < list.size(); i++) {
            result[i] = list.get(i);
        }
        return result;
    }

    /**
     * 反转顺序
     *
     * @param items
     */
    private void reverse(double items[]) {
        for (int i = 0; i < (items.length + 1) / 2; i++) {
            double x = 0;
            x = items[i];
            items[i] = items[items.length - 1 - i];
            items[items.length - i - 1] = x;
        }
    }
}
